#include "utility.h"

#include <STEPControl_Reader.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <TopExp.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Builder.hxx>
#include <TopoDS_Compound.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <Geom_Curve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Geom_Surface.hxx>

std::vector<TopoDS_Shape> IO::ImprtSTEP(const std::string& filename)
{
    STEPControl_Reader reader;
    TCollection_AsciiString  aFilePath = filename.c_str();
    IFSelect_ReturnStatus status = reader.ReadFile(aFilePath.ToCString());
    if (IFSelect_RetDone != status)
    {
        return {  };
    }

    int nbr = reader.NbRootsForTransfer();
    for (Standard_Integer n = 1; n <= nbr; n++)
    {
        reader.TransferRoot(n);
    }

    std::vector<TopoDS_Shape> shapes(reader.NbShapes());
    for (size_t i = 0; i < shapes.size(); i++)
    {
        shapes[i] = reader.Shape(int(i + 1));
    }
    return shapes;
}

std::vector<TopoDS_Shape> ShapeExp::GetAllShapes(const TopoDS_Shape& s, TopAbs_ShapeEnum type)
{
    TopTools_IndexedMapOfShape map_shapes;
    TopExp::MapShapes(s, type, map_shapes);

    std::vector<TopoDS_Shape> shapes(map_shapes.cbegin(), map_shapes.cend());
    return shapes;
}

std::vector<Handle(Geom_Curve)> ShapeExp::GetAllCurves(const std::vector<TopoDS_Shape>& shapes)
{
    std::vector<TopoDS_Shape> edges;
    for (const auto& s : shapes)
    {
        std::vector<TopoDS_Shape> s_edges = GetAllShapes(s, TopAbs_EDGE);

        edges.insert(edges.end(), s_edges.cbegin(), s_edges.cend());
    }

    std::vector<Handle(Geom_Curve)> curves;
    for (const auto& e : edges)
    {
        if (e.IsNull() || TopAbs_EDGE != e.ShapeType())
        {
            continue;
        }

        double cf = 0, cl = 0;
        Handle(Geom_Curve) curve = BRep_Tool::Curve(TopoDS::Edge(e), cf, cl);
        if (!curve)
        {
            continue;
        }

        if (TopAbs_REVERSED == e.Orientation())
        {
            cf = curve->ReversedParameter(cl);
            cl = curve->ReversedParameter(cf);
            curve->Reverse();
        }
        if (cf < curve->FirstParameter() || cl > curve->LastParameter())
        {
            Handle(Geom_TrimmedCurve) tcurve = new Geom_TrimmedCurve(curve, cf, cl);
            curve = tcurve;
        }

        curves.emplace_back(curve);
    }
    return curves;
}

std::vector<Handle(Geom_Surface)> ShapeExp::GetAllSurfaces(const std::vector<TopoDS_Shape>& shapes)
{
    std::vector<TopoDS_Shape> faces;
    for (const auto& s : shapes)
    {
        std::vector<TopoDS_Shape> map_faces = GetAllShapes(s, TopAbs_FACE);
        faces.insert(faces.end(), map_faces.cbegin(), map_faces.cend());
    }

    std::vector<Handle(Geom_Surface)> surfaces;
    for (const auto& s : faces)
    {
        if (s.IsNull() || TopAbs_FACE != s.ShapeType())
        {
            continue;
        }

        Handle(Geom_Surface) surf = BRep_Tool::Surface(TopoDS::Face(s));
        if (TopAbs_REVERSED == s.Orientation())
        {
            surf->UReverse();
        }

        surfaces.emplace_back(surf);
    }
    return surfaces;
}

TopoDS_Shape ShapeBuilder::Compound(const std::vector<TopoDS_Shape>& shapes)
{
    TopoDS_Builder builder;
    TopoDS_Compound comp;
    builder.MakeCompound(comp);
    for (const auto& s : shapes)
    {
        builder.Add(comp, s);
    }
    return comp;
}

TopoDS_Shape ShapeBuilder::PointCloud(const std::vector<gp_Pnt>& pnts)
{
    std::vector<TopoDS_Shape> vertices;
    for (const auto& p : pnts)
    {
        vertices.emplace_back(BRepBuilderAPI_MakeVertex(p));
    }

    return Compound(vertices);
}
